package json;

import javax.swing.JOptionPane;
import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * @author 黄乾坤
 * @create 2022-04-11 10:42
 */
public class JsonFormat {

    /** 空字符串 */
    private static final String EMPTY = "";
    /** 空格 */
    private static final char SPACE = ' ';
    /** 制表符 */
    private static final char TAB = '\t';
    /** 单位缩进字符串 */
    private static final String INDENTATION = "    ";
    /** 回车符 */
    private static final char ENTER = '\r';
    /** 换行符 */
    private final static char NEW_LINE = '\n';

    public static void main(String[] args) {
        if(args.length == 0) {
            JOptionPane.showMessageDialog(null, "请配置JSON格式化的文件地址！", "JSON格式化失败", JOptionPane.ERROR_MESSAGE);
            return;
        }
        JOptionPane.showMessageDialog(null, args[0], "文件路径", JOptionPane.INFORMATION_MESSAGE);
        try (InputStreamReader isr = new InputStreamReader(new FileInputStream(args[0]), StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(isr)){
            StringBuilder text = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null){
                text.append(line);
            }
            System.out.println(format(text.toString()));
        } catch (FileNotFoundException e) {
            JOptionPane.showMessageDialog(null, "文件不存在！", "JSON格式化失败", JOptionPane.ERROR_MESSAGE);
            return;
        } catch (IOException e) {
            JOptionPane.showMessageDialog(null, "读取文件内容出错！", "JSON格式化失败", JOptionPane.ERROR_MESSAGE);
            return;
        }
    }

    public static String format(String json) {
        StringBuilder result = new StringBuilder();

        int length = json.length();
        char key;
        Character wrapChar = null; //不为空说明当前 key 是单引号或双引号中间的字符串内容
        boolean isEscapeMode = false; //为 true 说明是单引号或双引号中间的转义字符

        //先去除json字符串中所有无用的空格、制表符、换行符
        boolean startRemove = true; //为 true 说明接下来的空格、制表符、换行符都不需要
        for (int i = 0; i < length; i++) {
            key = json.charAt(i);
            //如果是空格 并且 startRemove 为 true 则不需要该字符
            if(startRemove && (SPACE == key || TAB == key || ENTER == key || NEW_LINE == key)){
                continue;
            }
            if('"' == key || '\'' == key) {
                if(null == wrapChar) {
                    //字符串模式开始
                    wrapChar = key;
                    startRemove = false;
                }else if(isEscapeMode) {
                    //在字符串模式下的转义
                    isEscapeMode = false;
                }else if(wrapChar.equals(key)){
                    //字符串包装结束
                    wrapChar = null;
                    startRemove = true;
                }
                result.append(key);
                continue;
            }else if('\\' == key) {
                if(null != wrapChar) {
                    //字符串模式下转义有效
                    isEscapeMode = !isEscapeMode;
                    result.append(key);
                    continue;
                }else {
                    result.append(key);
                }
            }
            if(null != wrapChar) {
                //字符串模式
                result.append(key);
                continue;
            }
            //如果当前字符是冒号：
            if(key == ':'){
                startRemove = true;
                result.append(key);
                continue;
            }
            //如果当前字符是前方括号、前花括号：
            if ((key == '[') || (key == '{')) {
                startRemove = true;
                result.append(key);
                continue;
            }
            //如果当前字符是后方括号、后花括号：
            if ((key == ']') || (key == '}')) {
                startRemove = true;
                result.append(key);
                continue;
            }
            //如果当前字符是逗号：
            if ((key == ',')) {
                startRemove = true;
                result.append(key);
                continue;
            }
            //都不是时追加当前字符。
            startRemove = false;
            result.append(key);
        }

        json = result.toString();
        result = new StringBuilder();
        length = json.length();
        int number = 0; //缩进次数

        for (int i = 0; i < length; i++) {
            key = json.charAt(i);

            if('"' == key || '\'' == key) {
                if(null == wrapChar) {
                    //字符串模式开始
                    wrapChar = key;
                }else if(isEscapeMode) {
                    //在字符串模式下的转义
                    isEscapeMode = false;
                }else if(wrapChar.equals(key)){
                    //字符串包装结束
                    wrapChar = null;
                }
                result.append(key);
                continue;
            }else if('\\' == key) {
                if(null != wrapChar) {
                    //字符串模式下转义有效
                    isEscapeMode = !isEscapeMode;
                    result.append(key);
                    continue;
                }else {
                    result.append(key);
                }
            }

            if(null != wrapChar) {
                //字符串模式
                result.append(key);
                continue;
            }

            //如果当前字符是前方括号、前花括号做如下处理：
            if ((key == '[') || (key == '{')) {
                //如果前面还有字符，并且字符为“：”，打印：换行和缩进字符字符串。
                if ((i - 1 > 0) && (json.charAt(i - 1) == ':')) {
                    result.append(NEW_LINE);
                    result.append(indent(number));
                }
                result.append(key);
                //前方括号、前花括号，的后面必须换行。打印：换行。
                result.append(NEW_LINE);
                //每出现一次前方括号、前花括号；缩进次数增加一次。打印：新行缩进。
                number++;
                result.append(indent(number));

                continue;
            }

            // 3、如果当前字符是后方括号、后花括号做如下处理：
            if ((key == ']') || (key == '}')) {
                // （1）后方括号、后花括号，的前面必须换行。打印：换行。
                result.append(NEW_LINE);
                // （2）每出现一次后方括号、后花括号；缩进次数减少一次。打印：缩进。
                number--;
                result.append(indent(number));
                // （3）打印：当前字符。
                result.append(key);
                // （4）如果当前字符后面还有字符，并且字符不为“，”，打印：换行。
                if (((i + 1) < length) && (json.charAt(i + 1) != ',')) {
                    result.append(NEW_LINE);
                }
                // （5）继续下一次循环。
                continue;
            }

            // 4、如果当前字符是逗号。逗号后面换行，并缩进，不改变缩进次数。
            if ((key == ',')) {
                result.append(key);
                result.append(NEW_LINE);
                result.append(indent(number));
                continue;
            }
            // 5、打印：当前字符。
            result.append(key);
        }

        return result.toString();
    }

    /**
     * 返回指定次数的缩进字符串。每一次缩进4个空格，即SPACE。
     *
     * @param number 缩进次数。
     * @return 指定缩进次数的字符串。
     */
    private static String indent(int number) {
        if (number <= 0) {
            return EMPTY;
        }

        // 检查
        final int len = INDENTATION.length();
        final long longSize = (long) len * (long) number;
        final int size = (int) longSize;
        if (size != longSize) {
            throw new ArrayIndexOutOfBoundsException("Required String length is too large: " + longSize);
        }

        final char[] array = new char[size];
        INDENTATION.getChars(0, len, array, 0);
        int n;
        for (n = len; n < size - n; n <<= 1) {// n <<= 1相当于n *2
            System.arraycopy(array, 0, array, n, n);
        }
        System.arraycopy(array, 0, array, n, size - n);
        return new String(array);
    }
}
